<template>
  <div class="xtx-select" :style="{width:width}" ref="target">
    <div class="curr" @click="toggleMenu">
      <div class="select" v-if="label">{{label}}</div>
      <div class="ph" v-else>{{placeholder}}</div>
      <span class="iconfont icon-angle-down" :class="{up:open}"></span>
    </div>
    <div class="list" v-show="open">
      <template v-if="options.length">
        <div class="item" @click="change(item)" v-for="item in options" :key="item[optionValue]">
          {{item[optionLabel]}}
        </div>
      </template>
      <template v-else>
        <div class="none">暂无数据</div>
      </template>
    </div>
  </div>
</template>

<script>
import { computed, ref, toRefs } from 'vue'
import { onClickOutside, useVModel } from '@vueuse/core'
export default {
  name: 'XtxSelect',
  props: {
    // modelValue是接收的父组件传进来的值 选中的值value（或绑定的值）
    modelValue: {
      type: [String, Number],
      default: ''
    },
    // 组件width属性（宽度）
    width: {
      type: String,
      default: '300px'
    },
    // 组件placeholder属性（占位）
    placeholder: {
      type: String,
      default: '请选择'
    },
    // 组件options属性（选项）
    options: {
      type: Array,
      default: () => [], // 工厂函数 return 回一个默认值 等同function () { return [] }
      required: true
    },
    // 组件option-value属性（选项字段值）
    optionValue: {
      type: String,
      default: 'value'
    },
    // 组件option-label属性（选项字段名）
    optionLabel: {
      type: String,
      default: 'label'
    }
  },
  setup (props, { emit }) {
    const open = ref(false)
    const handlerClose = () => {
      open.value = false
    }
    const toggleMenu = () => {
      if (open.value) {
        handlerClose()
      } else {
        open.value = true
      }
    }
    // 点击组件外部其他区域函数
    const target = ref(null)
    onClickOutside(target, () => {
      handlerClose()
    })
    // 使用vueuse的useVModel做数据双向绑定, 修改updateValue 通知父组件更新
    const updateValue = useVModel(props, 'modelValue', emit)
    const { options, optionLabel, optionValue } = toRefs(props)
    const label = computed(() => {
      const obj = options.value.find(item => item[optionValue.value] === updateValue.value)
      return obj ? obj[optionLabel.value] : ''
    })
    const change = (item) => {
      // 如果选项字段值与选中值一致即返回
      if (item[optionValue.value] === updateValue.value) return
      // emit('input', item[props.optionValue])
      // emit('change', item[props.optionValue])
      // Vue3.0中自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件 通过emit('update:modelValue', '***')来改变外部对象的值
      emit('update:modelValue', item[optionValue.value])
      handlerClose()
    }
    return { open, toggleMenu, label, updateValue, change, target, handlerClose }
  }
}
</script>

<style lang="less" scoped>
// 自定义下拉框
.container {
  width: 1240px;
  margin: 0 auto;
  .xtx-select {
  width: 300px;
  height: 50px;
  position: relative;
  display: inline-block;
  user-select: none;
  .curr {
    width: 100%;
    height: 50px;
    padding-left: 10px;
    padding-right: 30px;
    border: 1px solid #e4e4e4;
    overflow: hidden;
    transition: all .4s .2s;
    .ph {
      color: #cccccc;
    }
    .iconfont {
      position: absolute;
      font-size: 18px;
      top: 0;
      right: 10px;
      color: #cccccc;
      transition: all 0.5s;
      &.up {
        transform: rotate(-180deg);
      }
    }
  }
  .list {
    position: absolute;
    left: 0;
    top: 50px;
    border: 1px solid #e4e4e4;
    border-top: none;
    width: 100%;
    max-height: 200px;
    overflow-y: auto;
    background: #ffffff;
    z-index: 998;
    .none {
      color: #999;
      text-align: center;
      line-height: 80px;
    }
    .item {
      line-height: 40px;
      height: 40px;
      width: 100%;
      overflow: hidden;
      padding-left: 10px;
      cursor: pointer;
      color: #666666;
      &:hover {
        background: #f5f5f5;
      }
    }
  }
}
}
</style>
